package TrafficSim;

import java.io.File;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.Vector;



public class Master {
	public enum augsburgSights {CITYGALERIE, MAN, FH, KOENIGSPLATZ, PLAERRER, SCHWABENCENTER, MAXSTRASSE, RANDOM};
	private int numberOfWorkers = getNrOfProcessors();
	private List<Worker> workerList;
	private VehicleLists vehicleLists = new VehicleLists(this);
	private Vector<Intersection> intersections = new Vector<Intersection>();
	private TrafficRenderer tr;
	private TrafficLightManager manager;
	private long timeForRendering = 0;
	private final long waitTime = 20;
	private final int algorithm = 0; // 0 = stupid dumb algorithm | 1 = more intelligent one
	public static boolean greenWave = true;
	private MainStreetManager mainStreetManager;
	private boolean running = true;
	private int numberOfCars = 1500;
	private int numberOfCarsInGreenWave = 2;
	private int counter = 0;

	public Master() {

		// Set up the traffic Graph
		TrafficGraph t = new TrafficGraph();
//		t.init();
		t.initAugsburg();
//		t.initMichi();
		this.intersections = t.getGraph();
		Vector <TrafficLightController> controllerList = new Vector<TrafficLightController>();
		Vector <Intersection> mainStreetIntersections = new Vector<Intersection>();
		Vector<Intersection> route = new Vector<Intersection>();
		
		for (Intersection intersection : this.intersections){
			TrafficLightController tempTLC = intersection.getTLC();
//			if (tempTLC != null){
				int [] a = {1,2,3,4,69,70,463,114,90,91,342,341,338,337,386,312,311,293,292,291,258,259,260,244};
				
				Vector<Integer> v = new Vector<Integer>();
				for (int i = 0; i<a.length; i++){
					v.add(a[i]);
					route.add(intersections.get(a[i]-1));
				}
				
				

				if (v.contains(intersection.getId())){
					if (Master.greenWave)
						mainStreetIntersections.add(intersection);
					else 
						if (tempTLC != null){
							controllerList.add(tempTLC);
						}
				}
				else{
					if (tempTLC != null){
					controllerList.add(tempTLC);
					}
				}// normal
				}
//		Vehicle vh1 = new Vehicle(3.0f, 100, 7.0f, route);
//		vehicleLists.addVehicle(vh1);
//				boolean found = false;
//				for (int i = 0; i<a.length; i++){
//					if (intersection.getId() == a[i]){
//						mainStreetControllerList.add(i, tempTLC);
//						found = true;
//					}
//				}
//				if (found == false){
//					controllerList.add(tempTLC);
//				}
//				for (TrafficLightController tl : controllerList){
//					System.out.println(tl.);
//				}

		if (Master.greenWave){
			this.mainStreetManager = new MainStreetManager(mainStreetIntersections);
		}
		this.manager = new TrafficLightManager(controllerList);
		// create some Cars
		if (Master.greenWave){
			Vehicle v = new Vehicle(3.0f, 100, 7.0f, this.mainStreetManager.getSortedList());
			vehicleLists.addVehicle(v);
			for (int i = 0; i < this.numberOfCarsInGreenWave/10; i++) {		
				Vehicle vh = new Vehicle(3.0f, 100, 7.0f, this.mainStreetManager.getSortedList());
				vehicleLists.addVehicle(vh);
			}
			Vector<Intersection> list2 = new Vector<Intersection>(this.mainStreetManager.getSortedList());
			
			for (int i = 0; i < 5; i++){
				list2.remove(0);
			}
			for (int i = 0; i < this.numberOfCarsInGreenWave/2; i++) {		
				Vehicle vh = new Vehicle(3.0f, 100, 7.0f, list2);
				vehicleLists.addVehicle(vh);
			}
			Vector<Intersection> list3 = new Vector<Intersection>(this.mainStreetManager.getSortedList());
			for (int i = 0; i < 10; i++){
				list3.remove(0);
			}
			for (int i = 0; i < this.numberOfCarsInGreenWave/2; i++) {		
				Vehicle vh = new Vehicle(3.0f, 100, 7.0f, list3);
				vehicleLists.addVehicle(vh);
			}
		}
		

		for (int i = 0; i < numberOfCars; i++) {
			Vehicle vh = new Vehicle(3.0f, 100, 7.0f, getRandomRoute());
			vehicleLists.addVehicle(vh);
		}
		
		
//		Vector <Intersection> testRoute = new Vector<Intersection>();  	//Test
//		testRoute.add(this.intersections.get(3));						//Auto
//		testRoute.add(this.intersections.get(8));						//das ueber
//		testRoute.add(this.intersections.get(3));		
//		testRoute.add(this.intersections.get(4));						//gruene 
//		Vehicle testVeh = new Vehicle(3.0f, 100, 7.0f, testRoute);		//Ampel 
//		vehicleLists.addVehicle(testVeh);								//faehrt
		
		
		// liste erstellen
		tr = new TrafficRenderer(intersections, vehicleLists, this);
		workerList = new Vector<Worker>();
		for (int a = 0; a < numberOfWorkers; a++) {
			workerList.add(new Worker(vehicleLists));
		}
	}

	public void calculate() {
		
//		System.out.println(counter);
		if (running){
		vehicleLists.fillTasklist();		
		for (int i = 0; i < numberOfWorkers; i++) {
			synchronized (workerList.get(i)) {
				// System.out.println("Worker aufwecken");
				workerList.get(i).notify();
			}
		}
		try {
			synchronized (this) {
				if (!(vehicleLists.getCloneList().size() == vehicleLists
						.getNumberOfVehicles())) {
					wait();
					// System.out.println("Master wurde aufgeweckt");
				}
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		vehicleLists.refreshList();
		this.manager.switchLights(algorithm);
		if (Master.greenWave){
			this.mainStreetManager.doIt();
			}
		}
		long timeBeforeRender = System.currentTimeMillis();
		if (timeForRendering < waitTime){
			try {
				long timeToWait = waitTime-timeForRendering; 
//				System.out.println("waiting "+ timeToWait + "ms");
				Thread.sleep(timeToWait);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		if (Master.greenWave){
			this.insertCars(numberOfCarsInGreenWave);
		}
		
		tr.render();
		timeForRendering = System.currentTimeMillis()-timeBeforeRender;		
//		System.out.println("rendered for "+timeForRendering+"ms");
	}

	public int getNrOfProcessors() {

		Runtime runtime = Runtime.getRuntime();
		int nrOfProcessors = runtime.availableProcessors();
//		System.out.println(nrOfProcessors);
		return nrOfProcessors;
	}
	public void pause(){
		if (this.running){
			this.running = false;
		}else{
			this.running = true;
		}
	}

	public static void main(String[] args) {

		Master master = new Master();
		File file = new File ("output.txt");
		file.delete();
		
		try {
			Thread.sleep(60);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		while (true) {
//			if (master.doingSomething){
			master.calculate();
//			}else{
//				try {
//					Thread.sleep(10);
//				} catch (InterruptedException e) {
//					// TODO Auto-generated catch block
//					e.printStackTrace();
//				}
//			}
		}
	}

	private Vector<Intersection> getRandomRoute() {
		Vector<Intersection> route = new Vector<Intersection>();
		int numberOfIntersections = (int) (Math.random() * 200 + 20);
//		int numberOfIntersections = 4;
		route.add(this.intersections
				.get((int) (Math.random() * (this.intersections.size()))));
		Intersection lastIntersection = null;
		Intersection temp = null;
		for (int i = 0; i < numberOfIntersections; i++) {
			Set<Intersection> nextIntersections = route.get(i)
					.getNextIntersections();
			Vector<Intersection> intersections = new Vector<Intersection>();
			lastIntersection = temp;
			temp = route.lastElement();
			for (Intersection j : nextIntersections){
				if (j == lastIntersection){continue;}
				Street s = temp.getStreet(j) ;
				int x = s.getPriority();
				for (int y = 0; y<x;y++){
					intersections.add(j);
				}
			}
			Iterator<Intersection> it = intersections.iterator();
			Intersection nextIntersection = null;
			try{
				nextIntersection = it.next();
			}
			catch(NoSuchElementException ex){
				nextIntersection = lastIntersection;
			}
			int random = (int) (Math.random() * intersections.size()-1);
			for (int j = 0; j < random; j++) {
				nextIntersection = it.next();
			}
			route.add(nextIntersection);
		}

		return route;
	}
	public void startEvent(augsburgSights start, int numberOfCars){
		pause();
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		for (int i = 0; i<numberOfCars;i++){
			Vehicle v = new Vehicle(3.0f, 100, 7.0f, getRandomRouteFrom(getIntersectionNear(start)));
			vehicleLists.addVehicle(v);
		}
		this.pause();
	}
	private Intersection getIntersectionNear(augsburgSights s){
		Vector<Intersection>cityGalerie = new Vector<Intersection>();
		Vector<Intersection>FH = new Vector<Intersection>();
		Vector<Intersection>Schwabencenter = new Vector<Intersection>();
		Vector<Intersection>MAN = new Vector<Intersection>();
		Vector<Intersection>Plaerrer = new Vector<Intersection>();
		Vector<Intersection>Maxstrasse = new Vector<Intersection>();
		Vector<Intersection>Koenigsplatz = new Vector<Intersection>();
		cityGalerie.add(intersections.get(296));
		cityGalerie.add(intersections.get(295));
		cityGalerie.add(intersections.get(294));
		cityGalerie.add(intersections.get(292));
		cityGalerie.add(intersections.get(291));
		cityGalerie.add(intersections.get(255));
		FH.add(intersections.get(242));
		FH.add(intersections.get(236));
		FH.add(intersections.get(237));
		FH.add(intersections.get(230));
		FH.add(intersections.get(245));
		FH.add(intersections.get(240));
		Schwabencenter.add(intersections.get(181));
		Schwabencenter.add(intersections.get(164));
		Schwabencenter.add(intersections.get(165));
		Schwabencenter.add(intersections.get(163));
		Schwabencenter.add(intersections.get(179));
		Schwabencenter.add(intersections.get(180));
		MAN.add(intersections.get(69));
		MAN.add(intersections.get(71));
		MAN.add(intersections.get(4));
		MAN.add(intersections.get(36));
		MAN.add(intersections.get(72));
		MAN.add(intersections.get(68));
		Plaerrer.add(intersections.get(446));
		Plaerrer.add(intersections.get(442));
		Plaerrer.add(intersections.get(443));
		Plaerrer.add(intersections.get(447));
		Plaerrer.add(intersections.get(456));
		Plaerrer.add(intersections.get(458));
		Plaerrer.add(intersections.get(453));
		Plaerrer.add(intersections.get(451));
		Plaerrer.add(intersections.get(448));
		Plaerrer.add(intersections.get(445));
		Maxstrasse.add(intersections.get(407));
		Maxstrasse.add(intersections.get(408));
		Maxstrasse.add(intersections.get(282));
		Maxstrasse.add(intersections.get(400));
		Maxstrasse.add(intersections.get(394));
		Maxstrasse.add(intersections.get(287));
		Maxstrasse.add(intersections.get(389));
		Koenigsplatz.add(intersections.get(411));
		Koenigsplatz.add(intersections.get(412));
		Koenigsplatz.add(intersections.get(409));
		Koenigsplatz.add(intersections.get(397));
		Koenigsplatz.add(intersections.get(405));
		
		
		switch (s){
		case CITYGALERIE:
			return cityGalerie.get((int)(Math.random()*cityGalerie.size()-1));
		case FH:
			return FH.get((int)(Math.random()*FH.size()-1));
		case MAN:
			return MAN.get((int)(Math.random()*MAN.size()-1));
		case MAXSTRASSE:
			return Maxstrasse.get((int)(Math.random()*Maxstrasse.size()-1));
		case PLAERRER:
			return Plaerrer.get((int)(Math.random()*Plaerrer.size()-1));
		case SCHWABENCENTER:
			return Schwabencenter.get((int)(Math.random()*Schwabencenter.size()-1));
		case KOENIGSPLATZ:
			return Koenigsplatz.get((int)(Math.random()*Koenigsplatz.size()-1));
		case RANDOM:
			return intersections.get((int)(Math.random()*intersections.size()-1));
		}
		return null;		
	}
	
	
	private Vector<Intersection> getRandomRouteFrom(Intersection start) {
		Vector<Intersection> route = new Vector<Intersection>();
		int numberOfIntersections = (int) (Math.random() * 0 + 500);
//		int numberOfIntersections = 4;
		route.add(start);
		Intersection lastIntersection = null;
		Intersection temp = null;
		for (int i = 0; i < numberOfIntersections; i++) {
			Set<Intersection> nextIntersections = route.get(i)
					.getNextIntersections();
			Vector<Intersection> intersections = new Vector<Intersection>();
			lastIntersection = temp;
			temp = route.lastElement();
			for (Intersection j : nextIntersections){
				if (j == lastIntersection){continue;}
				Street s = temp.getStreet(j) ;
				int x = s.getPriority();
				for (int y = 0; y<x;y++){
					intersections.add(j);
				}
			}
			Iterator<Intersection> it = intersections.iterator();
			Intersection nextIntersection = null;
			try{
				nextIntersection = it.next();
			}
			catch(NoSuchElementException ex){
				nextIntersection = lastIntersection;
			}
			int random = (int) (Math.random() * intersections.size()-1);
			for (int j = 0; j < random; j++) {
				nextIntersection = it.next();
			}
			route.add(nextIntersection);
		}

		return route;
	}
	
	public void insertCars (int cars){
		counter++;
		if (counter % 1000 == 0){
			startEvent(augsburgSights.RANDOM, 40);
			for (int i = 0; i < cars; i++) {		
				Vehicle vh = new Vehicle(3.0f, 100, 7.0f, this.mainStreetManager.getSortedList());
				vehicleLists.addVehicle(vh);
			}
			
		}
	}
}
